using System;
using System.Linq;
using System.Reflection;

using TrueCode.Core.Aspects;
using TrueCode.Core.Exceptions;

namespace TrueCode.Core.Validation
{
  public class Validator : IValidator
  {
    public void Validate( [NotNull] object @object )
    {
      if( @object == null ) throw new ArgumentNullException( "object" );

      var type = @object.GetType();
      foreach( var property in type.GetProperties() )
        ValidateProperty( @object, property );
    }

    public void Validate( [NotNull] object @object, [NotNull] string propertyName )
    {
      #region Validation
      if( @object == null ) throw new ArgumentNullException( "object" );
      if( propertyName == null ) throw new ArgumentNullException( "propertyName" );
      #endregion

      var type = @object.GetType();
      var property = type.GetProperty( propertyName );
      ValidateProperty( @object, property );
    }

    private static void ValidateProperty( object @object, PropertyInfo property )
    {
      var propertyType = property.PropertyType;
      if( propertyType == typeof( string ) )
        ValidateStringProperty( @object, property );
    }

    private static void ValidateStringProperty( object @object, PropertyInfo property )
    {
      var attributes = property.GetCustomAttributes( true );

      if( attributes.Length > 0 )
      {
        var notEmptyAttribute = (Validate.NotEmptyAttribute)attributes.SingleOrDefault( a => a.GetType() == typeof( Validate.NotEmptyAttribute ) );
        if( notEmptyAttribute != null )
        {
          if( string.IsNullOrEmpty( ( string )property.GetValue( @object, null ) ) )
            throw notEmptyAttribute.Exception ?? new ArgumentNullException( property.Name );
        }

        var maxLengthAttribute = ( Validate.MaxLengthAttribute )attributes.SingleOrDefault( a => a.GetType() == typeof( Validate.MaxLengthAttribute ) );
        if( maxLengthAttribute != null )
        {
          var value = ( string )property.GetValue( @object, null );
          if( value != null && value.Length > maxLengthAttribute.MaxLength )
            throw maxLengthAttribute.Exception ??
                  new TooLongException( maxLengthAttribute.MaxLength, value.Length, "The string is too long", property.Name );
        }
      }
    }
  }
}